package com.infoblazer.jimql;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: davidm
 * Date: 1/31/11
 * Time: 11:23 AM
 */
public class JimqlObject extends Jimql {
    private final Map<String, Map<Integer, Object>> hashCodes = new HashMap<String, Map<Integer, Object>>();//We are not maintaining hashcodes in the map version

    public JimqlObject(DBTYPE dbType) {
        super(dbType);
    }

    protected Map<String, Map<Integer, Object>> getHashCodes() {
        return hashCodes;
    }


    protected int loadColumnValue(PreparedStatement ps, int i, Object tuple, Column column) throws SQLException {

        Object object = null;
        try {
            object = column.getGetter().invoke(tuple);
        } catch (IllegalAccessException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (InvocationTargetException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        return super.loadColumnValueObject( ps,  i,  column,object);
    }


    protected Relation findColumns(String className, List relations) {

        Relation relation = new Relation();
        List<Column> columns = new ArrayList<Column>();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(relations.get(0).getClass());
            relation.setName(className);

            for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) {
                String methodName = methodDescriptor.getName();
                String propertyName = null;

                if (methodName.startsWith("get")) {
                    propertyName = methodName.substring(3);
                    propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
                } else if (methodName.startsWith("is")) {
                    propertyName = methodName.substring(2);
                }
                if (propertyName != null) {
                    Method getter = methodDescriptor.getMethod();


                    Class<?> methodType = getter.getReturnType();
                    Column column = new Column();
                    column.setMethodType(methodType);
                    column.setColumnName(propertyName);
                    column.setGetter(getter);


                    if (column.isSupportedType()) {
                        columns.add(column);
                    }
                }
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        relation.setColumns(columns);
        return relation;
    }

    protected List<Map<String, Object>> executeQuery(List<Relation> relations, String criteria, String orderBy) {
        ResultSet rs = null;
        PreparedStatement ps = null;
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();


        try {
            StringBuilder sb = new StringBuilder();

            sb.append("select * from ");
            int i = 1;
            for (Relation relation : relations) {
                sb.append(relation.getName());
                if (i < relations.size()) {
                    sb.append(", ");
                }
                i++;
            }
            if (criteria != null && !criteria.isEmpty()) {
                sb.append(" WHERE ");
                sb.append(criteria);
            }
            if (orderBy != null && !orderBy.isEmpty()) {
                sb.append(" Order BY ");

                sb.append(orderBy);
            }


            ps = conn.prepareStatement(sb.toString());

            rs = ps.executeQuery();

            while (rs.next()) {
                Map<String, Object> map = new HashMap<String, Object>();
                for (Relation relation : relations) {
                    Object object = hashCodes.get(relation.getName()).get(rs.getInt(relation.getName() + "_" + HASHCODE_COL));
                    map.put(relation.getName(), object);
                }
                result.add(map);
            }

        } catch (SQLException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (ps != null) {
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }


        }
        return result;
    }


    public List<Map<String, Object>> loadAndQuery(String criteria, String orderBy, List... dataLists) {
        loadData(dataLists);
        return queryData(criteria, orderBy, dataLists);

    }

    public List<Map<String, Object>> queryData(String criteria, String orderBy, List... dataLists) {

        List<Map<String, Object>> result;

        List<Relation> relations = findRelations(dataLists);

        createIndexes(relations, criteria);

        result = executeQuery(relations, criteria, orderBy);
        return result;
    }


     public ResultSet queryData(String sql) {
          ResultSet rs = null;
        PreparedStatement ps = null;
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();


        try {
            StringBuilder sb = new StringBuilder(sql);

            ps = conn.prepareStatement(sb.toString());

            rs = ps.executeQuery();


        } catch (SQLException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } finally {
            try {
                if (ps != null) {
                    ps.close();
                }

            } catch (SQLException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }


        }
        return rs;



     }

}
